import { CanActivate, ExecutionContext, ForbiddenException, Inject, Injectable, UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { Observable, from } from 'rxjs';
import { pathToRegexp } from 'path-to-regexp';
import { UserService } from '@/module/user/user.service';
import { RedisService } from '@/module/redis/redis.service';
import { Prefix } from '../enum';
import { Request } from 'express'
declare module 'express' {
    interface Request {
        userId: string | number
    }
}

interface IJwtPayload {
    id: string | number;
    iat: number;
}
@Injectable()
export class AuthGuard implements CanActivate {
    @Inject()
    private redisService: RedisService
    @Inject()
    private jwtService: JwtService;
    @Inject()
    private userService: UserService;
    private globalWhiteList = [];

    constructor(private readonly config: ConfigService) {
        this.globalWhiteList = this.config.get('perm.router.whitelist') || []
        console.log(this.globalWhiteList);
    }

    async canActivate(ctx: ExecutionContext): Promise<boolean> {
        const req: Request = ctx.switchToHttp().getRequest();
        const isInWhiteList = this.checkWhiteList(req)
        if (isInWhiteList) return true;

        const loginToken = ctx.switchToHttp().getRequest().get("Authorization");
        if (!loginToken) throw new ForbiddenException("请重新登录")

        const info: IJwtPayload = await this.userService.parseToken(loginToken);
        if (!info) throw new UnauthorizedException("当前登录已过期，请重新登录")

        const redisJSON = await this.redisService.get(`${Prefix.loginToken}${info.id}`);
        const redisUserInfo = JSON.parse(redisJSON)

        if (!redisUserInfo || redisUserInfo.iat != info.iat) throw new UnauthorizedException("当前登录已过期，请重新登录")
        req.userId = info.id
        this.redisService.getTtl(`${Prefix.loginToken}${info.id}`).then((ttl) => {
            // 自动续期
            if (ttl < 86400) {
                this.redisService.expire(`${Prefix.loginToken}${info.id}`, 259200)
            }
        })

        return true;
    }

    /**
  * 检查接口是否在白名单内
  * @param ctx
  * @returns
  */
    checkWhiteList(req: Request): boolean {
        const i = this.globalWhiteList.findIndex((route) => {
            // 请求方法类型相同
            if (req.method.toUpperCase() === route.method.toUpperCase()) {
                const url = req.url.split('?')[0];

                // 对比 url
                return !!pathToRegexp(route.path).exec(url);
            }
            return false;
        });
        // 在白名单内 则 进行下一步， i === -1 ，则不在白名单，需要 比对是否有当前接口权限
        return i > -1;
    }
}
